package simple;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
public class SimpleBizMsgCrypt {

    private byte[] aesKey;
    private String clientId;
    private String token;

    private final static String TOKEN = "YW5kIHRvZGF5LCB5b3U";
    private final static String CLIENT_ID = "YW5kIHllc3RlcmRheSBhIGRlZXI";
    private final static String ENCODING_AES_KEY = "RGF5IGJlZm9yZSB5ZXN0ZXJkYXkgSSBzYXcgYSByYWJ";
    private static Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    public final static String REAL_DATA = "real_data";
    public final static String FROM_CLIENT_ID = "from_client_id";
    public final static String CHECK_CLIENT_ID = "check_client_id";

    public SimpleBizMsgCrypt() {
        this.aesKey = Base64.decodeBase64(ENCODING_AES_KEY + "=");
        this.clientId = CLIENT_ID;
        this.token = TOKEN;
    }

    public SimpleBizMsgCrypt(String token, String encodingAesKey, String clientId) {
        if (encodingAesKey == null || encodingAesKey.length() != 43) {
            throw new RuntimeException("encodingAesKey非法！");
        }
        this.aesKey = Base64.decodeBase64(encodingAesKey + "=");
        this.clientId = clientId;
        this.token = token;
    }

    public String signature(List<String> signList) {

        signList.add(token);
        String[] signArr = signList.toArray(new String[signList.size()]);
        // 请求参数字典序（不是参数名的字典序）
        Arrays.sort(signArr);
        StringBuilder sb = new StringBuilder();
        for (String s : signArr) {
            sb.append(s);
        }
        return AlgorithmUtils.sha1(sb.toString());
    }

    public boolean verifySignature(String signature, List<String> signList) {
        if (StringUtils.isBlank(signature)
                || signList == null
                || signList.size() == 0) {
            return false;
        }
        signList.add(token);
        String[] signArr = signList.toArray(new String[signList.size()]);
        // 请求参数字典序（不是参数名的字典序）
        Arrays.sort(signArr);
        StringBuilder sb = new StringBuilder();
        for (String s : signArr) {
            sb.append(s);
        }
        String sha1 = AlgorithmUtils.sha1(sb.toString());
        return signature.equalsIgnoreCase(sha1);
    }

    /**
     * 对明文进行加密.
     *
     * @param data 需要加密的明文
     * @return 加密后base64编码的字符串
     */
    public String encrypt(String data) {

        String randomStr = getRandomStr();
        ByteGroup byteCollector = new ByteGroup();
        byte[] randomStrBytes = randomStr.getBytes(DEFAULT_CHARSET);
        byte[] textBytes = data.getBytes(DEFAULT_CHARSET);
        byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
        byte[] client_idBytes = clientId.getBytes(DEFAULT_CHARSET);

        // 组成格式，randomStr + networkBytesOrder + text + client_id
        byteCollector.addBytes(randomStrBytes);
        byteCollector.addBytes(networkBytesOrder);
        byteCollector.addBytes(textBytes);
        byteCollector.addBytes(client_idBytes);

        // ... + pad: 使用自定义的填充方式对明文进行补位填充
        byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
        byteCollector.addBytes(padBytes);

        // 获得最终的字节流, 未加密
        byte[] unencrypted = byteCollector.toBytes();

        String algorithm = "AES/CBC/NoPadding";
        String keyAlgorithm = "AES";
        byte[] ivArr = Arrays.copyOfRange(aesKey, 0, 16);

        try {
            // AES加密
            byte[] encrypted = AlgorithmUtils.aesEncrypt(unencrypted, aesKey, algorithm, keyAlgorithm, ivArr);
            // BASE64编码
            String base64Encrypted = new Base64().encodeToString(encrypted);

            return base64Encrypted;
        } catch (Exception e) {
            log.error("Error {}", algorithm, e);
        }
        return null;
    }

    public Map<String, Object> decrypt(String data) {

        Map<String, Object> resultMap = new HashMap<>();
        String algorithm = "AES/CBC/NoPadding";
        String keyAlgorithm = "AES";
        try {
            // BASE64解码
            byte[] encrypted = Base64.decodeBase64(data);
            byte[] ivArr = Arrays.copyOfRange(aesKey, 0, 16);
            // AES解密
            byte[] original = AlgorithmUtils.aesDecrypt(encrypted, aesKey, algorithm, keyAlgorithm, ivArr);
            // 去除补位字符
            byte[] bytes = PKCS7Encoder.decode(original);
            // 分离16位随机字符串,网络字节序和AppId
            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
            int xmlLength = recoverNetworkBytesOrder(networkOrder);

            String hash = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), DEFAULT_CHARSET);
            String from_client_id = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), DEFAULT_CHARSET);
            resultMap.put(REAL_DATA, hash);
            resultMap.put(FROM_CLIENT_ID, from_client_id);
            resultMap.put(CHECK_CLIENT_ID, from_client_id.equals(clientId));
        } catch (Exception e) {
            log.error("Error {}", algorithm, e);
        }
        return resultMap;
    }

    /**
     * 随机生成16位字符串
     */
    public String getRandomStr() {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 16; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 生成4个字节的网络字节序
     *
     * @param sourceNumber
     * @return
     */
    private byte[] getNetworkBytesOrder(int sourceNumber) {
        byte[] orderBytes = new byte[4];
        orderBytes[3] = (byte) (sourceNumber & 0xFF);
        orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
        orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
        orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
        return orderBytes;
    }

    /**
     * 还原4个字节的网络字节序
     *
     * @param orderBytes
     * @return
     */
    private int recoverNetworkBytesOrder(byte[] orderBytes) {
        int sourceNumber = 0;
        for (int i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= orderBytes[i] & 0xff;
        }
        return sourceNumber;
    }


    public static void main(String[] args) {

        String authorize = HttpUtil.get("https://bbgxcxpreview.hzgjbbg.cn/prod-api/auth/simple/authorize/nonce");
        JSONObject authorizeObject = JSONObject.parseObject(authorize);
        Integer code = authorizeObject.getInteger("code");
        JSONObject data = authorizeObject.getJSONObject("data");
        String nonce = data.getString("nonce");
        String timestamp = data.getString("timestamp");


        String dataStr = "{\"phone\":\"13212345678\"}";
        SimpleBizMsgCrypt simpleCrypt = new SimpleBizMsgCrypt();


        List<String> signList = new ArrayList<>();
        signList.add(timestamp);
        signList.add(nonce);
        String signature = simpleCrypt.signature(signList);

        String encrypt = simpleCrypt.encrypt(dataStr);
        List<String> dataSignList = new ArrayList<>();
        dataSignList.add(timestamp);
        dataSignList.add(nonce);
        dataSignList.add(encrypt);
        String dataSignature = simpleCrypt.signature(dataSignList);

        Map<String, Object> param = new HashMap<>();
        param.put(SimpleHttpParameters.NONCE, nonce);
        param.put(SimpleHttpParameters.TIMESTAMP, timestamp);
        param.put(SimpleHttpParameters.SIGNATURE, signature);
        param.put(SimpleHttpParameters.DATA_SIGNATURE, dataSignature);
        Map<String, Object> body = new HashMap<>();
        body.put(SimpleHttpParameters.ENCRYPT, encrypt);
        Map<String, Object> result = new HashMap<>();
        result.put("param", param);
        result.put("body", body);
//        System.out.println(JSON.toJSONString(result, SerializerFeature.PrettyFormat));

        String format = StrUtil.format(
                "https://bbgxcxpreview.hzgjbbg.cn/prod-api/auth/simple/authorize/accessToken?nonce={}&timestamp={}&signature={}&data_signature={}",
                param.get(SimpleHttpParameters.NONCE),
                param.get(SimpleHttpParameters.TIMESTAMP),
                param.get(SimpleHttpParameters.SIGNATURE),
                param.get(SimpleHttpParameters.DATA_SIGNATURE)
                );

        String post = HttpUtil.post(format, JSON.toJSONString(body, SerializerFeature.PrettyFormat));
        System.out.println(post);

    }


}
